package com.mrzhou.auth.filter;


import com.mrzhou.auth.authorzation.IntegrationAuthenticationService;
import com.mrzhou.auth.exception.IntegrationException;
import com.mrzhou.auth.support.IntegrationAuthenticationContext;
import com.mrzhou.auth.support.IntegrationAuthenticationEntity;
import com.mrzhou.auth.utils.TokenUtil;
import com.mrzhou.core.tool.utils.StringUtil;
import com.mrzhou.web.support.RequestParameterWrapper;
import com.mrzhou.web.util.WebUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;

/**
 * 集合认证拦截器
 * 	作用：
 * 		1. 根据参数获取当前的是认证类型
 * 		2. 根据不同的认证类型调用不同的IntegrationAuthenticator.prepar进行预处理
 */
@Component
public class IntegrationAuthenticationFilter implements Filter {

    /** 需要处理的验证方式 */
    private Collection<IntegrationAuthenticationService> authenticators;

    private final ApplicationContext applicationContext;

    public IntegrationAuthenticationFilter(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = initRequest((HttpServletRequest) req);
        HttpServletResponse response = (HttpServletResponse) res;
        System.out.println(request.getRequestURI());
        // 将此处请求的参数保存到IntegrationAuthenticationEntity中, 便于后续代码可以获取其他参数
        IntegrationAuthenticationEntity iae = IntegrationAuthenticationContext.get();

        String authType = request.getParameter(TokenUtil.AUTH_TYPE_PARAM);
        String grantType = request.getParameter(TokenUtil.GRANT_TYPE_PARAM);

        // 获取登录方式
        iae.setAuthType(StringUtil.isNotBlank(authType) ? authType: grantType);
        // 保存请求中的参数信息
        iae.setAuthParameterMap(request);
        // 获取请求的真实ip
        iae.setAuthParameter(TokenUtil.CLIENT_IP_PARAM, new String[]{WebUtil.getIp(request)});
        iae.setAuthHeaderMap(request);
        // 在上下文中保存此次请求中的参数信息
        IntegrationAuthenticationContext.set(iae);

        try{
            //预处理
            this.prepare(iae);

            // 预处理完毕后放行请求
            filterChain.doFilter(request, response);

            //后置处理
            this.complete(iae);

        } catch (IntegrationException e) {
            request.setAttribute(TokenUtil.ERROR_PARAM, e.getLocalizedMessage());
            request.getRequestDispatcher(TokenUtil.ERROR_HANDLER_URL).forward(request, response);
        } finally {
            //无论是否正常完成业务处理, 都释放上下文中保存的信息
            IntegrationAuthenticationContext.clear();
        }
    }

    /**
     * 预处理, 对一些参数的校验; 在请求进入Controller是前处理
     */
    private void prepare(IntegrationAuthenticationEntity authEntity) {
        // 检查登录类型
        for (IntegrationAuthenticationService authenticator: authenticators) {
            if (authenticator.support(authEntity)){
                authenticator.prepare(authEntity);
            }
        }

    }

    /**
     * 后置处理
     * @param authEntity
     */
    private void complete(IntegrationAuthenticationEntity authEntity) {
        for (IntegrationAuthenticationService authenticator: authenticators) {
            if (authenticator.support(authEntity)){
                authenticator.complete(authEntity);
            }
        }
    }

    /**
     * 设置请求的默认参数
     * @return
     */
    private HttpServletRequest initRequest(HttpServletRequest request) {
        RequestParameterWrapper wrapper = new RequestParameterWrapper(request);
        // 如果grant_type不为空且为refresh_token这按框架原有逻辑处理
        String grantType = request.getParameter(TokenUtil.GRANT_TYPE_PARAM);
        if (StringUtil.isNotBlank(grantType) && TokenUtil.AUTH_REFRESH_TOKEN.equals(grantType)) {
            return request;
        }
        wrapper.addParameter(TokenUtil.GRANT_TYPE_PARAM, TokenUtil.AUTH_PASSWORD);
        return wrapper;
    }

    /**
     * 在当前bean创建完成后载入模块中的认证方式
     */
    @PostConstruct
    public void init() {
        if(null == this.authenticators) {
            synchronized (this) {
                // 获取IntegrationAuthenticationService的实现类Bean
                Map<String, IntegrationAuthenticationService> authServiceMap = this.applicationContext.getBeansOfType(IntegrationAuthenticationService.class);
                if (null != authServiceMap) {
                    this.authenticators = authServiceMap.values();
                }
            }
        }
    }


}
